import uuid
import hmac
import json
import hashlib
import redis
import tornado.web


class Session:
    def __init__(self):
        self.session_id = None
        self.hmac_key = None
        self._data = {}

    def get(self, k, default=None):
        return self._data.get(k, default)

    def set(self, k, v):
        self._data[k] = v


class SessionManager:
    def __init__(self, secret, cache: redis.StrictRedis, session_timeout):
        self.secret = secret
        self.session_timeout = session_timeout
        self.cache = cache

    def _fetch(self, session_id):
        try:
            session_data = raw_data = self.cache.get(session_id)
            if raw_data is not None:
                self.cache.setex(session_id, self.session_timeout, raw_data)
                session_data = json.loads(raw_data)

            if isinstance(session_data, dict):
                return session_data
            else:
                return {}
        except IOError:
            return {}

    # 解析校验session 没有返回空
    def get(self, request_handler: tornado.web.RequestHandler):
        verification = request_handler.request.headers.get("user_token", None)
        try:
            session_id = verification[:64]
            hmac_key = verification[65:]
        except Exception as e:
            return None

        check_hmac = self._generate_hmac(session_id)
        if hmac_key != check_hmac:
            return None

        data = self._fetch(session_id)
        session = Session()
        session.session_id = session_id
        session.hmac_key = hmac_key
        for k, v in data.items():
            session.set(k, v)
        return session

    def save(self, session):
        tmp = {"user_token": "{}:{}".format(session.session_id, session.hmac_key)}
        session_data = json.dumps(session._data)
        self.cache.setex(session.session_id, self.session_timeout, session_data)
        return tmp

    def delete(self, session):
        self.cache.delete(session.session_id)

    def _generate_id(self):
        new_id = hashlib.sha256(self.secret + uuid.uuid4().bytes)
        return new_id.hexdigest()

    def _generate_hmac(self, session_id):
        return hmac.new(session_id.encode(), self.secret, hashlib.sha256).hexdigest()

    def gen_session(self):
        session = Session()
        session.session_id = self._generate_id()
        session.hmac_key = self._generate_hmac(session.session_id)
        return session


class InvalidSessionException(Exception):
    pass
